package project.elements.order;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import project.elements.base.Job;
import project.elements.base.Machine;
import project.elements.base.Task;
import project.elements.priority.PriorityCalculator;

public class OpenShop {
	public static List<String> schedule(List<Job> jobs, PriorityCalculator priorityCalculator) {
		List<String> result = new ArrayList<String>();
		Machine[] machines = getMachinesFromJobs(jobs);
		for (int i = 0; i < machines.length; i++) {
			System.out.println(machines[i]);
		}
		long time = 0;
		boolean hasUncompleteJobs = false;

		do {
			// result.add("time: " + time);
			jobs = BubbleSort.singleton().orderJobs(jobs, priorityCalculator);
			// System.out.println("Time " + time);
			// for (Job job : jobs) {
			// System.out.println(job);
			// }
			// System.out.println();
			// Utermezes
			for (Machine machine : machines) {
				if (machine.isFree() == false) {
					machine.work();
					continue;
				}
				// Job keresese adott gepre
				for (int i = 0; i < jobs.size(); i++) {
					if (isJobDone(jobs.get(i), time) == true) {
						continue;
					}
					if (isJobInProgress(jobs.get(i), time)) {
						continue;
					}
					if (time < jobs.get(i).getOnsetTime()) {
						continue; // Meg nem erkezett be a job.
					}
					// A jobot ki lehet utemezni legalabb 1 gepre
					Task task = jobs.get(i).getTaskByMachine(machine);
					if (task == null) {
						continue;
					}
					if (task.getStartTime() != -1) {
						continue;
					}

					// A job kiutemezheto az adott gepre, mert van ilyen task a
					// job-ban es meg nincsen keszen.
					machine.setJob(jobs.get(i));
					task.start(time);
					// result.add("machine: " + machine.getName() + " Job: " +
					// jobs.get(i).getName() + " Time: " + time +
					// " Action: Start");
					// result.add("machine: " + machine.getName() + " Job: " +
					// jobs.get(i).getName() + " Time: " + (time +
					// jobs.get(i).getTaskByMachine(machine).getCost()) +
					// " Action: Stop");
					result.add("" + machine.getName() + ";" + jobs.get(i).getName() + ";" + time + ";Start");
					result.add("" + machine.getName() + ";" + jobs.get(i).getName() + ";" + (time + jobs.get(i).getTaskByMachine(machine).getCost()) + ";Stop");

					break;
				}
				machine.work();
			}

			// Kell a kovetkezo iteracio
			hasUncompleteJobs = false;
			for (Job job : jobs) {
				if (isJobDone(job, time) == false) {
					hasUncompleteJobs = true;
					break;
				}
			}
			time++;

		} while (hasUncompleteJobs);
		result.add("Makespan: " + (time - 1));
		result.add("Avg Idle Time: " + getAvgIdleTime(machines));
		result.add("Max Idle Time: " + getMaxIdleTime(machines));
		result.add("Avg Work Time: " + getAvgWorkTime(machines));
		result.add("Max Work Time: " + getMaxWorkTime(machines));
		result.add("Utilization of machines: " + getSumMachineUtilization(machines) + "%");

		// for (Machine machine : machines) {
		// System.out.println(machine.toString());
		// }

		return result;
	}

	private static Machine[] getMachinesFromJobs(List<Job> jobs) {
		Set<Machine> machines = new HashSet<Machine>();
		for (Job j : jobs) {
			for (Task t : j.getnextTasks()) {
				machines.add(t.getMachine());
			}
		}
		return machines.toArray(new Machine[machines.size()]);
	}

	private static boolean isJobDone(Job job, long time) {
		for (Task task : job.getTasks()) {
			if (task.getStartTime() == -1) {
				return false;
			}
			if (task.getEndTime() > time) {
				return false;
			}
		}
		return true;
	}

	private static boolean isJobInProgress(Job job, long time) {
		for (Task task : job.getTasks()) {
			if (task.getStartTime() == -1) {
				continue;
			}
			if (task.getEndTime() > time) {
				return true;
			}
		}
		return false;
	}

	private static double getAvgIdleTime(Machine[] machines) {
		double result = 0.0;
		for (Machine machine : machines) {
			result += machine.getIdleTime();
		}
		return result / machines.length;
	}

	private static long getMaxIdleTime(Machine[] machines) {
		long result = 0;
		for (Machine machine : machines) {
			if (result < machine.getIdleTime()) {
				result = machine.getIdleTime();
			}
		}
		return result;
	}

	private static double getAvgWorkTime(Machine[] machines) {
		double result = 0.0;
		for (Machine machine : machines) {
			result += machine.getMachineActualTime();
		}
		return result / machines.length;
	}

	private static long getMaxWorkTime(Machine[] machines) {
		long result = 0;
		for (Machine machine : machines) {
			if (result < machine.getMachineActualTime()) {
				result = machine.getMachineActualTime();
			}
		}
		return result;
	}

	public static double getSumMachineUtilization(Machine[] machines) {
		double resultA = 0.0;
		double resultB = 0.0;
		for (Machine machine : machines) {
			resultA += machine.getMachineActualTime();
		}
		for (Machine machine : machines) {
			resultB += machine.getIdleTime();
		}
		resultB += resultA;
		resultB = (resultA / resultB) * 100;
		return resultB;

	}

}
